package zone

import (
	"github.com/Unixeno/TheMoon/moon/core/context"
	"github.com/Unixeno/TheMoon/moon/zone/storage"
	"sync"
	"sync/atomic"
)

type collectorDB struct {
	sync.Map
}

var collector collectorDB

type counterType int

const (
	volumeTypeCounter counterType = iota
	networkTypeCounter
	imageTypeCounter
)

type counter struct {
	zoneID   string
	num      *int32
	category counterType
}

func (c *collectorDB) init() {
	m := storage.ListCollectError()
	ctx, _ := context.NewContext()
	for id, entry := range m {
		var err error
		category := counterType(entry.Category)
		d, ok := getDeployer(entry.ZoneID)
		if !ok {
			continue
		}
		switch category {
		case volumeTypeCounter:
			err = d.removeVolume(ctx, id)
		case imageTypeCounter:
			err = d.removeImage(ctx, id)
		case networkTypeCounter:
			err = d.removeNetwork(ctx, id)
		}
		if err == nil || err == errImageNotExist || err == errVolumeNotExist {
			storage.DeleteCollectError(id)
		} else {
			storage.PutCollectError(id, storage.LocalCollectError{ZoneID: entry.ZoneID, Category: int(category)})
			c.loadOrCreate(id, category)
		}
	}
}

func (c *counter) add() {
	atomic.AddInt32(c.num, 1)
}

func (c *counter) sub() {
	atomic.AddInt32(c.num, -1)
}

func (c *collectorDB) loadOrCreate(id string, category counterType) *counter {
	v, _ := c.LoadOrStore(id, &counter{
		num:      new(int32),
		category: category,
	})
	return v.(*counter)
}

func (c *collectorDB) collect() {
	ctx, _ := context.NewContext()
	c.Range(func(key interface{}, value interface{}) bool {
		e := value.(*counter)
		d, ok := getDeployer(e.zoneID)
		if !ok {
			return true
		}
		if atomic.LoadInt32(e.num) <= 0 {
			id := key.(string)
			var err error
			switch e.category {
			case volumeTypeCounter:
				err = d.removeVolume(ctx, id)
			case imageTypeCounter:
				err = d.removeImage(ctx, id)
			case networkTypeCounter:
				err = d.removeNetwork(ctx, id)
			}
			if err != nil && err != errImageNotExist && err != errVolumeNotExist {
				storage.PutCollectError(id, storage.LocalCollectError{
					Category: int(e.category),
				})
			} else {
				c.Delete(key)
			}
		}
		return true
	})
}

func (c *collectorDB) get(id string) (*counter, bool) {
	e, ok := c.Load(id)
	if ok {
		return e.(*counter), ok
	}
	return nil, ok
}
